From f4606f266bff57bc832d6e5affc361d1783fc16f Mon Sep 17 00:00:00 2001 From: Bastien Nocera Date: Sun, 25 Nov 2007 18:07:01 +0000 Subject: [PATCH] Add detection for libjasper, used by the gdk-pixbuf JPEG2000 loader 2007-11-25 Bastien Nocera * configure.in: Add detection for libjasper, used by the gdk-pixbuf JPEG2000 loader 2007-11-25 Bastien Nocera * Makefile.am: * io-jasper.c: Add the libjasper JPEG2000 loader (Closes: #469901) svn path=/trunk/; revision=19042 --- ChangeLog | 5 + configure.in | 19 ++- gdk-pixbuf/ChangeLog | 6 + gdk-pixbuf/Makefile.am | 22 ++- gdk-pixbuf/io-jasper.c | 305 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 354 insertions(+), 3 deletions(-) create mode 100644 gdk-pixbuf/io-jasper.c diff --git a/ChangeLog b/ChangeLog index e80f2d747b..e933ff91bc 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2007-11-25 Bastien Nocera + + * configure.in: Add detection for libjasper, used by the + gdk-pixbuf JPEG2000 loader + 2007-11-25 Matthias Clasen * configure.in: Require gtk-doc 1.8 diff --git a/configure.in b/configure.in index 9c2cc9b833..e17ba8478e 100644 --- a/configure.in +++ b/configure.in @@ -757,6 +757,9 @@ AC_ARG_WITH(libjpeg, AC_ARG_WITH(libtiff, [AC_HELP_STRING([--without-libtiff], [disable TIFF loader for gdk-pixbuf])]) +AC_ARG_WITH(libjasper, + [AC_HELP_STRING([--without-libjasper], + [disable JPEG2000 loader for gdk-pixbuf])]) dnl Test for libtiff if test x$with_libtiff != xno && test -z "$LIBTIFF"; then @@ -858,9 +861,21 @@ dnl Test for libpng *** from CVS.]) fi +dnl Test for libjasper + if test x$with_libjasper != xno && test -z "$LIBJASPER"; then + AC_CHECK_LIB(jasper, jas_init, LIBJASPER=-ljasper, []) + fi + + if test x$with_libjasper != xno && test -z "$LIBJASPER"; then + AC_MSG_ERROR([ +*** Checks for JPEG2000 loader failed. You can build without it by passing +*** --without-libjasper to configure]) + fi + AC_SUBST(LIBTIFF) AC_SUBST(LIBJPEG) AC_SUBST(LIBPNG) +AC_SUBST(LIBJASPER) AM_CONDITIONAL(BUILD_DYNAMIC_MODULES, $dynworks) @@ -886,7 +901,7 @@ else fi fi -all_loaders="png,bmp,wbmp,gif,ico,ani,jpeg,pnm,ras,tiff,xpm,xbm,tga,pcx,icns" +all_loaders="png,bmp,wbmp,gif,ico,ani,jpeg,pnm,ras,tiff,xpm,xbm,tga,pcx,icns,jasper" included_loaders="" # If no loaders specified, include all if test "x$with_included_loaders" = xyes ; then @@ -931,6 +946,7 @@ AM_CONDITIONAL(INCLUDE_XBM, [test x"$INCLUDE_xbm" = xyes]) AM_CONDITIONAL(INCLUDE_TGA, [test x"$INCLUDE_tga" = xyes]) AM_CONDITIONAL(INCLUDE_PCX, [test x"$INCLUDE_pcx" = xyes]) AM_CONDITIONAL(INCLUDE_ICNS, [test x"$INCLUDE_icns" = xyes]) +AM_CONDITIONAL(INCLUDE_JASPER, [test x"$INCLUDE_jasper" = xyes]) AC_HEADER_SYS_WAIT @@ -939,6 +955,7 @@ AC_TYPE_SIGNAL AM_CONDITIONAL(HAVE_TIFF, test "x$LIBTIFF" != x) AM_CONDITIONAL(HAVE_PNG, test "x$LIBPNG" != x) AM_CONDITIONAL(HAVE_JPEG, test "x$LIBJPEG" != x) +AM_CONDITIONAL(HAVE_JASPER, test "x$LIBJASPER" != x) if $dynworks ; then STATIC_LIB_DEPS= diff --git a/gdk-pixbuf/ChangeLog b/gdk-pixbuf/ChangeLog index 02e60f8153..f3be42fb6c 100644 --- a/gdk-pixbuf/ChangeLog +++ b/gdk-pixbuf/ChangeLog @@ -1,3 +1,9 @@ +2007-11-25 Bastien Nocera + + * Makefile.am: + * io-jasper.c: Add the libjasper JPEG2000 loader + (Closes: #469901) + 2007-11-20 Matthias Clasen * gdk-pixbuf-animation.c: Use G_DEFINE_TYPE (#469341, diff --git a/gdk-pixbuf/Makefile.am b/gdk-pixbuf/Makefile.am index 4e7d780de4..272cc31501 100644 --- a/gdk-pixbuf/Makefile.am +++ b/gdk-pixbuf/Makefile.am @@ -181,6 +181,14 @@ libpixbufloader_pcx_la_SOURCES = io-pcx.c libpixbufloader_pcx_la_LDFLAGS = -avoid-version -module $(no_undefined) libpixbufloader_pcx_la_LIBADD = $(module_libs) +# +# The JPEG2000 loader +# +libstatic_pixbufloader_jasper_la_SOURCES = io-jasper.c +libpixbufloader_jasper_la_SOURCES = io-jasper.c +libpixbufloader_jasper_la_LDFLAGS = -avoid-version -module $(no_undefined) +libpixbufloader_jasper_la_LIBADD = $(LIBJASPER) $(module_libs) + if HAVE_PNG if INCLUDE_PNG STATIC_PNG_LIB = libstatic-pixbufloader-png.la @@ -277,6 +285,14 @@ else ICNS_LIB = libpixbufloader-icns.la endif +if HAVE_JASPER +if INCLUDE_JASPER +STATIC_JASPER_LIB = libstatic-pixbufloader-jasper.la +else +JASPER_LIB = libpixbufloader-jasper.la +endif +endif + if BUILD_DYNAMIC_MODULES loader_LTLIBRARIES = \ @@ -294,7 +310,8 @@ loader_LTLIBRARIES = \ $(XBM_LIB) \ $(TGA_LIB) \ $(ICNS_LIB) \ - $(PCX_LIB) + $(PCX_LIB) \ + $(JASPER_LIB) endif @@ -314,7 +331,8 @@ noinst_LTLIBRARIES = \ $(STATIC_XBM_LIB) \ $(STATIC_TGA_LIB) \ $(STATIC_ICNS_LIB) \ - $(STATIC_PCX_LIB) + $(STATIC_PCX_LIB) \ + $(STATIC_JASPER_LIB) builtin_objs = @INCLUDED_LOADER_OBJ@ diff --git a/gdk-pixbuf/io-jasper.c b/gdk-pixbuf/io-jasper.c new file mode 100644 index 0000000000..5194ebd001 --- /dev/null +++ b/gdk-pixbuf/io-jasper.c @@ -0,0 +1,305 @@ +/* JPEG 2000 loader + * + * Copyright (c) 2007 Bastien Nocera + * Inspired by work by Ben Karel + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include +#include +#include + +#include "gdk-pixbuf-private.h" +#include "gdk-pixbuf-io.h" + +#include + +G_MODULE_EXPORT void fill_vtable (GdkPixbufModule * module); +G_MODULE_EXPORT void fill_info (GdkPixbufFormat * info); + +struct jasper_context { + GdkPixbuf *pixbuf; + + GdkPixbufModuleSizeFunc size_func; + GdkPixbufModuleUpdatedFunc updated_func; + GdkPixbufModulePreparedFunc prepared_func; + gpointer user_data; + + jas_stream_t *stream; + + int width, height; +}; + +static void +free_jasper_context (struct jasper_context *context) +{ + if (!context) + return; + + if (context->stream) { + jas_stream_close (context->stream); + context->stream = NULL; + } + + g_free (context); +} + +static gpointer +jasper_image_begin_load (GdkPixbufModuleSizeFunc size_func, + GdkPixbufModulePreparedFunc prepared_func, + GdkPixbufModuleUpdatedFunc updated_func, + gpointer user_data, GError **error) +{ + struct jasper_context *context; + jas_stream_t *stream; + + jas_init (); + + stream = jas_stream_memopen (NULL, -1); + if (!stream) { + g_set_error (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY, _("Couldn't allocate memory for stream")); + return NULL; + } + + context = g_new0 (struct jasper_context, 1); + if (!context) + return NULL; + + context->size_func = size_func; + context->updated_func = updated_func; + context->prepared_func = prepared_func; + context->user_data = user_data; + context->width = context->height = -1; + + context->stream = stream; + + return context; +} + +static gboolean +jasper_image_try_load (struct jasper_context *context, GError **error) +{ + jas_image_t *raw_image, *image; + int num_components, colourspace_family; + int i, rowstride, shift; + guchar *pixels; + + raw_image = jas_image_decode (context->stream, -1, 0); + if (!raw_image) { + g_set_error (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_CORRUPT_IMAGE, _("Couldn't decode image")); + return FALSE; + } + + if (context->width == -1 && context->height == -1) { + int width, height; + + context->width = width = jas_image_cmptwidth (raw_image, 0); + context->height = height = jas_image_cmptheight (raw_image, 0); + + if (context->size_func) { + (*context->size_func) (&width, &height, context->user_data); + + if (width == 0 || height == 0) { + jas_image_destroy(raw_image); + g_set_error (error, + GDK_PIXBUF_ERROR, + GDK_PIXBUF_ERROR_CORRUPT_IMAGE, + _("Transformed JPEG2000 has zero width or height")); + return FALSE; + } + } + } + + /* We only know how to handle grayscale and RGB images */ + num_components = jas_image_numcmpts (raw_image); + colourspace_family = jas_clrspc_fam (jas_image_clrspc (raw_image)); + + if ((num_components != 3 && num_components != 4 && num_components != 1) || + (colourspace_family != JAS_CLRSPC_FAM_RGB && colourspace_family != JAS_CLRSPC_FAM_GRAY)) { + jas_image_destroy (raw_image); + g_set_error (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_UNKNOWN_TYPE, _("Image type currently not supported")); + return FALSE; + } + + /* Apply the colour profile to the image, creating a new one */ + if (jas_image_clrspc (raw_image) != JAS_CLRSPC_SRGB) { + jas_cmprof_t *profile; + + profile = jas_cmprof_createfromclrspc (JAS_CLRSPC_SRGB); + if (!profile) { + jas_image_destroy (raw_image); + g_set_error (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY, _("Couldn't allocate memory for color profile")); + return FALSE; + } + + image = jas_image_chclrspc (raw_image, profile, JAS_CMXFORM_INTENT_PER); + if (!image) { + jas_image_destroy (raw_image); + g_set_error (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY, _("Couldn't allocate memory for color profile")); + return FALSE; + } + } else { + image = raw_image; + } + + if (!context->pixbuf) { + int bits_per_sample; + guchar *data; + + /* Unfortunately, gdk-pixbuf doesn't support 16 bpp images + * bits_per_sample = jas_image_cmptprec (image, 0); + if (bits_per_sample < 8) + bits_per_sample = 8; + else if (bits_per_sample > 8) + bits_per_sample = 16; + */ + bits_per_sample = 8; + + data = g_try_malloc0 (context->width * context->height * bits_per_sample / 8); + if (data == NULL) { + g_set_error (error, + GDK_PIXBUF_ERROR, + GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY, + _("Insufficient memory to open JPEG 2000 file")); + return FALSE; + } + context->pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, + FALSE, bits_per_sample, + context->width, context->height); + if (context->prepared_func) + context->prepared_func (context->pixbuf, NULL, context->user_data); + } + + /* We calculate how much we should shift the pixel + * data by to make it fit into our pixbuf */ + shift = MAX (jas_image_cmptprec (image, 0) - gdk_pixbuf_get_bits_per_sample (context->pixbuf), 0); + + /* Loop over the 3 colourspaces */ + rowstride = gdk_pixbuf_get_rowstride (context->pixbuf); + pixels = gdk_pixbuf_get_pixels (context->pixbuf); + + for (i = 0; i < num_components; i++) { + jas_matrix_t *matrix; + int j; + + matrix = jas_matrix_create (context->height, context->width); + + /* in libjasper, R is 0, G is 1, etc. we're lucky :) + * but we need to handle the "opacity" channel ourselves */ + if (i != 4) { + jas_image_readcmpt (image, i, 0, 0, context->width, context->height, matrix); + } else { + jas_image_readcmpt (image, JAS_IMAGE_CT_OPACITY, 0, 0, context->width, context->height, matrix); + } + + for (j = 0; j < context->height; j++) { + int k; + + for (k = 0; k < context->width; k++) { + if (num_components == 3 || num_components == 4) { + pixels[j * rowstride + k * 3 + i] = jas_matrix_get (matrix, j, k) >> shift; + } else { + pixels[j * rowstride + k * 3] = + pixels[j * rowstride + k * 3 + 1] = + pixels[j * rowstride + k * 3 + 2] = jas_matrix_get (matrix, j, k) >> shift; + } + } + /* Update once per line for the last component, otherwise + * we might contain garbage */ + if (context->updated_func && (i == num_components - 1) && k != 0) { + context->updated_func (context->pixbuf, 0, j, k, 1, context->user_data); + } + } + + jas_matrix_destroy (matrix); + } + + if (image != raw_image) + jas_image_destroy (image); + jas_image_destroy (raw_image); + + return TRUE; +} + +static gboolean +jasper_image_stop_load (gpointer data, GError **error) +{ + struct jasper_context *context = (struct jasper_context *) data; + gboolean ret; + + jas_stream_rewind (context->stream); + ret = jasper_image_try_load (context, error); + + free_jasper_context (context); + + return ret; +} + +static gboolean +jasper_image_load_increment (gpointer data, const guchar *buf, guint size, GError **error) +{ + struct jasper_context *context = (struct jasper_context *) data; + + if (jas_stream_write (context->stream, buf, size) < 0) { + g_set_error (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY, _("Couldn't allocate memory to buffer image data")); + return FALSE; + } + + return TRUE; +} + +void +fill_vtable (GdkPixbufModule * module) +{ + module->begin_load = jasper_image_begin_load; + module->stop_load = jasper_image_stop_load; + module->load_increment = jasper_image_load_increment; +} + +void +fill_info (GdkPixbufFormat * info) +{ + static GdkPixbufModulePattern signature[] = { + { " jP", "!!!! ", 100 }, /* file begins with 'jP' at offset 4 */ + { "\xff\x4f\xff\x51\x00", NULL, 100 }, /* file starts with FF 4F FF 51 00 */ + { NULL, NULL, 0 } + }; + static gchar *mime_types[] = { + "image/jp2", + "image/jpeg2000", + "image/jpx", + NULL + }; + static gchar *extensions[] = { + "jp2", + "jpc", + "jpx", + "j2k", + "jpf", + NULL + }; + + info->name = "jpeg2000"; + info->signature = signature; + info->description = N_("The JPEG 2000 image format"); + info->mime_types = mime_types; + info->extensions = extensions; + info->flags = GDK_PIXBUF_FORMAT_THREADSAFE; + info->license = "LGPL"; + info->disabled = FALSE; +} + -- 2.30.2